import fs from "node:fs/promises";
import path from "node:path";

const WORKFLOW_ROOT = path.resolve(__dirname, "..", "..");

type Blueprint = Record<string, unknown>;

interface YamlModule {
  parse: (input: string) => unknown;
  stringify?: (value: unknown) => string;
}

async function loadYamlModule(): Promise<YamlModule> {
  try {
    const yaml = await import("yaml");
    return { parse: yaml.parse, stringify: yaml.stringify };
  } catch {
    try {
      const jsYaml = await import("js-yaml");
      return {
        parse: (input: string) => jsYaml.load(input),
        stringify: (value: unknown) => jsYaml.dump(value),
      };
    } catch (error) {
      throw new Error("Unable to parse YAML. Please install 'yaml' or 'js-yaml'.");
    }
  }
}

export async function loadStructuredData(filePath: string): Promise<unknown> {
  const absolutePath = path.isAbsolute(filePath)
    ? filePath
    : path.join(WORKFLOW_ROOT, filePath);
  const raw = await fs.readFile(absolutePath, "utf-8");
  if (filePath.endsWith(".json")) {
    return JSON.parse(raw);
  }
  if (filePath.endsWith(".yaml") || filePath.endsWith(".yml")) {
    const yamlModule = await loadYamlModule();
    return yamlModule.parse(raw);
  }
  throw new Error(`Unsupported file format: ${filePath}`);
}

function pickSections(source: Record<string, unknown>, sections?: string[]): Record<string, unknown> {
  if (!sections || sections.length === 0) {
    return source;
  }

  const result: Record<string, unknown> = {};
  for (const section of sections) {
    const pathSegments = section.split(/[.>/]/g).map((segment) => segment.trim()).filter(Boolean);
    let cursor: any = source;
    let valid = true;
    for (const segment of pathSegments) {
      if (cursor && typeof cursor === "object" && segment in cursor) {
        cursor = (cursor as Record<string, unknown>)[segment];
      } else {
        valid = false;
        break;
      }
    }

    if (valid) {
      result[section] = cursor;
    }
  }
  return result;
}

export async function readBlueprint(blueprintPath: string, sections?: string[]): Promise<Blueprint> {
  const data = await loadStructuredData(blueprintPath);
  if (!data || typeof data !== "object") {
    throw new Error(`Invalid blueprint data: ${blueprintPath}`);
  }

  const blueprint = data as Blueprint;
  return pickSections(blueprint, sections);
}

export async function filterFigmaData(
  yamlPath: string,
  predicate: (node: any) => boolean,
): Promise<any[]> {
  const data = await loadStructuredData(yamlPath);
  if (!data || typeof data !== "object") {
    throw new Error(`Invalid Figma data: ${yamlPath}`);
  }

  const nodes = Array.isArray((data as any).nodes)
    ? ((data as any).nodes as any[])
    : [];

  const filtered: any[] = [];
  for (const node of nodes) {
    try {
      if (predicate(node)) {
        filtered.push(node);
      }
    } catch {
      // ignore predicate errors for a single node but continue overall
    }
  }

  return filtered;
}

export async function extractDesignIntent(blueprintPath: string): Promise<{
  complexity: "simple" | "medium" | "complex";
  hasChart: boolean;
  hasForm: boolean;
  themeCount: number;
}> {
  const blueprint = await loadStructuredData(blueprintPath);
  if (!blueprint || typeof blueprint !== "object") {
    throw new Error("Blueprint is not an object");
  }

  const designIntent: any = (blueprint as Record<string, unknown>)["designIntent"] || {};
  const components: any[] = Array.isArray(designIntent.components) ? designIntent.components : [];
  const complexity: "simple" | "medium" | "complex" = designIntent.complexity ?? inferComplexity(components);
  const themes: any[] = Array.isArray(designIntent.themes) ? designIntent.themes : [];

  return {
    complexity,
    hasChart: components.some((component) => includesAny(component, ["chart", "line", "bar", "pie"])),
    hasForm: components.some((component) => includesAny(component, ["form", "input", "select"])),
    themeCount: themes.length || 1,
  };
}

function includesAny(component: any, keywords: string[]): boolean {
  if (!component) {
    return false;
  }
  const serialized = JSON.stringify(component).toLowerCase();
  return keywords.some((keyword) => serialized.includes(keyword));
}

function inferComplexity(components: any[]): "simple" | "medium" | "complex" {
  if (components.length <= 3) {
    return "simple";
  }
  if (components.length <= 7) {
    return "medium";
  }
  return "complex";
}

export async function writeJsonFile(targetPath: string, payload: unknown): Promise<void> {
  const absolutePath = path.isAbsolute(targetPath)
    ? targetPath
    : path.join(WORKFLOW_ROOT, targetPath);
  await fs.mkdir(path.dirname(absolutePath), { recursive: true });
  await fs.writeFile(absolutePath, JSON.stringify(payload, null, 2), "utf-8");
}

export async function writeYamlFile(targetPath: string, payload: unknown): Promise<void> {
  const absolutePath = path.isAbsolute(targetPath)
    ? targetPath
    : path.join(WORKFLOW_ROOT, targetPath);
  await fs.mkdir(path.dirname(absolutePath), { recursive: true });
  const yamlModule = await loadYamlModule();
  if (yamlModule.stringify) {
    await fs.writeFile(absolutePath, yamlModule.stringify(payload), "utf-8");
    return;
  }
  await fs.writeFile(absolutePath, JSON.stringify(payload, null, 2), "utf-8");
}
